package org.opens.guava.utilites.eventBus.myeventbus;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 说明:
 *      1. 设置包可可见, 不想对外开放的类就不加publish修饰;
 *      2. 这个类是用来存储MyEventBus使用register方法时, 保存信息的;
 */
class MyRegistryTable {

    private final ConcurrentHashMap<String, ConcurrentLinkedQueue<MySubscriber>> concurrentHashMap = new ConcurrentHashMap<>();

    public void bind(Object subscriber) {
        List<Method> subscribeMehtods = getScribeMethod(subscriber);
        subscribeMehtods.forEach(m -> {
            tierSubscriber(subscriber, m);
        });
    }

    public ConcurrentLinkedQueue<MySubscriber> scanSubscriber(final String topic) {
        return concurrentHashMap.get(topic);
    }

    public void unbind(Object subscriber) {
        concurrentHashMap.forEach((key, queue) -> queue.forEach(s -> {
            if(s.getMySubscribeObject() == subscriber) {
                s.setDisable(true);
            }
        }));
    }

    private void tierSubscriber(Object subscriber, Method method) {
        MySubscribe annotation = method.getDeclaredAnnotation(MySubscribe.class);
        String topic = annotation.topic();
        //这条指令的意思是判断topic对应的值是否存在, 如果不存在则执行构面的语句创建(topic-> new ConcurrentLinkedQueue<>()); 相当于是初始化.
        concurrentHashMap.computeIfAbsent(topic, key -> new ConcurrentLinkedQueue<>());
        concurrentHashMap.get(topic).add(new MySubscriber(subscriber, method));
    }

    private List<Method> getScribeMethod(Object subscriber) {
        final List<Method> methods = new ArrayList<>();
        Class<?> temp = subscriber.getClass();
        while(temp != null) {
            Method[] classMethods = temp.getDeclaredMethods();
            Arrays.stream(classMethods).filter(m -> m.isAnnotationPresent(MySubscribe.class) && m.getParameterCount() == 1 && m.getModifiers() == Modifier.PUBLIC).forEach(methods::add);
            temp = temp.getSuperclass();
        }
        return methods;
    }

}
